﻿///***************************************************************
/// 시  스  템 : SMFramework (based on Microsoft.Framework)
/// 단위시스템 : Configuration
/// 계      층 : Framework Configuration
/// 프로그램명 : AppSettingsReader
/// 파  일  명 : AppSettingsReader.cs
/// 설      명 : 설정파일(web.config)에서 설정값을 읽는다.
///***************************************************************
/// 수정ID 일자       이름     내용
///---------------------------------------------------------------
/// CH001  2008-07-02 송시명   최초 작성
///***************************************************************
///
using System;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Specialized;
using NsConfig = System.Configuration;

namespace SMFramework.Configuration
{
	/// <summary>
	/// xml 형태를 가진 계층적 구조를 가진 config 파일 내용을 읽어 오기 위한 Helper 클래스
	/// 이는 .NET 내장 객체인 System.Configuration.ConfigurationSettings 기능을 포함하고 있다.
	/// </summary>
	public class AppSettingsReader
	{
		#region enum
		/// <summary>
		/// 시스템이 초기에 가지고 있는 Config파일을 사용하는지
		/// 사용자가 지정한 Config파일을 사용하는지 나타낸다.
		/// 
		/// Default - 시스템과 사용자 지정이 같음
		/// Redirected - 시스템를 무시하고 사용자 지정을 구성
		/// Assigned - 시스템에 관계없이 사용자 지정으로 구성 
		/// </summary>
		public enum ConfigurationFileMode
		{
			/// <summary>
			/// 시스템과 사용자 지정이 같음
			/// </summary>
			Default,

			/// <summary>
			/// 시스템 설정을 사용자 지정을 대체
			/// </summary>
			Redirected,

			/// <summary>
			/// 시스템에 관계없이 사용자 지정으로 구성 
			/// </summary>
			Assigned
		}

		private const string SECTIONNAME_APPSETTINGS = "appSettings";
		private const string SECTIONNAME_APPSETTINGSREADER = "SMFramework/AppSettingsReader";
		private const string KEYNAME_RAISEEXCEPTION = "RaiseException";
		private const string ROOT_TAGNAME = "/configuration/";

		private static ConfigurationFileMode _staticConfigFileMode;
		private static string _staticConfigFile;
		private static bool _staticRaiseException;
		private static NameValueCollection _staticKeyValuesl;

		private ConfigurationFileMode _ConfigFileMode;
		private string _configFile;
		private string _sectionName;
		private bool _raiseException;
		private NameValueCollection _keyValues = null;
		
		#endregion
		
		#region 생성자

		static AppSettingsReader()
		{
			InternalInitialize(null);
		}

		/// <summary>
		/// AppSettingsReader 클래스 생성자로써 시스템이 찾아낸 Config파일를  사용
		/// </summary>
		public AppSettingsReader()
		{
			_ConfigFileMode = _staticConfigFileMode;
			_configFile = _staticConfigFile;
			_sectionName = null;
			_raiseException = _staticRaiseException;

			if (_ConfigFileMode == ConfigurationFileMode.Default)
			{
				_keyValues = null;
			}
			else
			{
				_keyValues = _staticKeyValuesl;
			}
		}

		/// <summary>
		/// AppSettingsReader 클래스 생성자로써 시스템이 찾아낸 Config파일 사용
		/// </summary>
		/// <param name="sectionName">config파일내에 XML.SelectSingleNode 시 사용되는 파라미터</param>
		public AppSettingsReader(string sectionName)
		{
			_ConfigFileMode = _staticConfigFileMode;
			_configFile = _staticConfigFile;
			_sectionName = sectionName;
			_raiseException = _staticRaiseException;
			_keyValues = GetKeyValueCollection(_configFile, sectionName);
		}

		/// <summary>
		/// AppSettingsReader 클래스 생성자로써 사용자가 직접 지정하는 경우에 사용
		/// </summary>
		/// <param name="configurationFile">config파일</param>
		/// <param name="sectionName">config파일내에 XML.SelectSingleNode 시 사용되는 파라미터</param>
		public AppSettingsReader(string configurationFile, string sectionName)
		{
			_ConfigFileMode = ConfigurationFileMode.Assigned;
			_configFile = configurationFile;
			_sectionName = sectionName;
			_raiseException = _staticRaiseException;
			_keyValues = GetKeyValueCollection(configurationFile, sectionName);
		}
		
		#endregion
		
		#region 초기화

		/// <summary>
		/// 지정된 어셈블리로부터 Config파일을 NameValueCollection 으로 읽어드림
		/// </summary>
		/// <param name="assemblyLocation">어셈블리 위치</param>
		private static void InternalInitialize(string assemblyLocation)
		{
			string configFilePath1 = System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
			string configFilePath2;

			if (assemblyLocation != null)
			{
				configFilePath2 = FindConfigurationFile(assemblyLocation);
			}
			else
			{
				configFilePath2 = configFilePath1;
			}

			if (configFilePath1 == configFilePath2)
			{
				_staticConfigFileMode = ConfigurationFileMode.Default;
				_staticConfigFile = configFilePath1;
			}
			else
			{
				_staticConfigFileMode = ConfigurationFileMode.Redirected;
				_staticConfigFile = configFilePath2;
			}

			_staticConfigFile = System.IO.Path.GetFullPath(_staticConfigFile);
			_staticRaiseException = true;

			NameValueCollection nvCol = GetKeyValueCollection(_staticConfigFile, SECTIONNAME_APPSETTINGSREADER);

			if (nvCol != null)
			{
				string raiseException = nvCol[KEYNAME_RAISEEXCEPTION];
				if (raiseException != null)
				{
					_staticRaiseException = Convert.ToBoolean(raiseException);
				}
			}

			if (_staticConfigFileMode != ConfigurationFileMode.Default)
			{
				_staticKeyValuesl = GetKeyValueCollection(_staticConfigFile, SECTIONNAME_APPSETTINGS);
			}
		}

		/// <summary>
		/// Static 클래스 초기화 메서드로써
		/// 현재 실행중인 어셈블리의 CodeBase 로 부터 Config파일을 NaveValueCollection 으로 읽어드림
		/// </summary>
		public static void Initialize()
		{
			string callingAssemlyLoacation
			   = Assembly.GetCallingAssembly().CodeBase;
			InternalInitialize(callingAssemlyLoacation);
		}
		
		#endregion
		
		#region 프로퍼티

		/// <value>
		/// config(XML파일)
		/// </value>
		public string ConfigurationFile
		{
			get
			{
				return _configFile;
			}
		}

		/// <value>
		/// config(XML파일)로 부터 SelectSingleNode를 위한 파라미터로써 사용됨
		/// </value>
		public string SectionName
		{
			get
			{
				return _sectionName;
			}
		}

		/// <value>
		/// GetValue 메서드를 통해서 value 값을 찾지 못한경우 예외처리 발생여부(true/false)
		/// </value>
		public bool RaiseException
		{
			get
			{
				return _raiseException;
			}
			set
			{
				_raiseException = value;
			}
		}

		/// <value>
		/// GetValue 메서드의 Type를 string 값으로 지정한것과 동일함
		/// </value>
		public string this[string key]
		{
			get
			{
				return (string)GetValue(key, typeof(string));
			}
		}

		/// <value>
		/// GetValue 메서드의 Type를 string 값으로 지정한것과 동일함, 기본값 설정
		/// </value>
		public string this[string key, string defaultValue]
		{
			get
			{
				return (string)GetValue(key, typeof(string), defaultValue);
			}
		}

		/// <value>
		/// GetValue 메서드의 Type를 int 값으로 지정한것과 동일함, 기본값 설정
		/// </value>
		public int this[string key, int defaultValue]
		{
			get
			{
				return (int)GetValue(key, typeof(int), defaultValue);
			}
		}

		/// <value>
		/// GetValue 메서드의 Type를 bool 값으로 지정한것과 동일함, 기본값 설정
		/// </value>
		public bool this[string key, bool defaultValue]
		{
			get
			{
				return (bool)GetValue(key, typeof(bool), defaultValue);
			}
		}

		/// <value>
		/// 현재 저장된 NameValueCollection 객체를 리턴
		/// </value>
		public NameValueCollection KeyValues
		{
			get
			{
				return _keyValues;
			}
		}
		
		#endregion

		#region GetValue

		/// <summary>
		/// Key값의 Value 객체를 찾는데 사용된다.
		/// .NET 의 AppSettingReader 와 함께 사용가능도록 설계되었다.
		/// </summary>
		/// <param name="key">찾고자 하는 키값</param>
		/// <param name="type">찾고자 하는 키의 Type</param>
		/// <returns>찾은 결과 객체</returns>
		public object GetValue(string key, Type type)
		{
			object valueObj = null;

			try
			{
				NsConfig.AppSettingsReader reader;
				if (_keyValues == null)
				{
					reader = new NsConfig.AppSettingsReader();
					valueObj = reader.GetValue(key, type);
				}
				else
				{
					string valueStr = _keyValues[key];
					if (valueStr != null)
					{
						valueObj = Convert.ChangeType(valueStr, type);
					}
					else
					{
						throw new InvalidOperationException();
					}
				}
			}
			catch (Exception ex)
			{
				valueObj = null;
				if (_raiseException == true)
				{
					throw ex;
				}
			}
			return valueObj;
		}

		/// <summary>
		/// Key값의 Value 객체를 찾는데 사용된다.
		/// </summary>
		/// <param name="key">찾고자 하는 키값</param>
		/// <param name="type">찾고자 하는 키의 Type</param>
		/// <param name="defaultValue">기본값 지정, 찾고자 하는 키값이 없을때 리턴값으로 사용된다</param>
		/// <returns>찾은 결과 객체</returns>
		public object GetValue(string key, Type type, object defaultValue)
		{
			object valueObj = null;

			try
			{
				valueObj = GetValue(key, type);
			}
			catch (InvalidOperationException)
			{
				valueObj = null;
			}

			if (valueObj == null)
			{
				return defaultValue;
			}
			else
			{
				return valueObj;
			}
		}
		
		#endregion

		#region FindConfigurationFile

		/// <summary>
		/// 지정한 어셈블리를 호스트 하는 프로세스(win2003 - w3wp.exe, WinXP,Win2000 - aspnet_wp.exe)를 
		/// 찾아서 해당 프로세스의 config 파일을 찾아낸다.
		/// 또한 해당 프로세스가 COM+ (dllhost.exe) 인경우도 config파일을 찾아올수 있다.   
		/// </summary>
		/// <param name="assemblyLocation">어셈블리위치</param>
		/// <returns>찾아낸 Config 파일</returns>
		private static string FindConfigurationFile(string assemblyLocation)
		{
			string configFileName = null;
			try
			{
				string processName
				   = Process.GetCurrentProcess().MainModule.ModuleName;
				string path = assemblyLocation;
				Uri uri = new Uri(path);
				path = Path.GetDirectoryName(uri.LocalPath);
				string assemblyName = Path.GetFileName(uri.LocalPath);

				for (int n = 0; n < 2; n++)
				{
					switch (processName.ToLower())
					{
						case "aspnet_wp.exe":
							goto case "w3wp.exe";
						case "w3wp.exe":
							configFileName = path + @"\web.config";
							break;
						case "dllhost.exe":
							configFileName = path + @"\dllhost.exe.config";
							if (File.Exists(configFileName) == false)
							{
								configFileName = path + @"\web.config";
							}
							break;
					}
					if (File.Exists(configFileName) == true)
					{
						break;
					}
					else
					{
						configFileName = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
					}
					path = path.Substring(0, path.LastIndexOf('\\'));
				}
			}
			catch (Exception)
			{
			}

			return configFileName;
		}
		
		#endregion

		#region GetKeyValueCollection

		/// <summary>
		/// 지정된 파일에 XML의 SelectSingNode를 호출 결과값을 NameValueCollection에 저장한다.
		/// </summary>
		/// <param name="filePath">config 파일 지정</param>
		/// <param name="sectionName">SelectSingNode의 파라미터로써 사용됨</param>
		/// <returns></returns>
		private static NameValueCollection GetKeyValueCollection(string filePath, string sectionName)
		{
			NameValueCollection nvCol = new NameValueCollection();

			try
			{
				XmlDocument xmlDoc = new XmlDocument();
				xmlDoc.Load(filePath);

				XmlNode node = xmlDoc.SelectSingleNode(ROOT_TAGNAME + sectionName);
				XmlNodeList nodeList = node.SelectNodes("add");
				foreach (XmlNode item in nodeList)
				{
					nvCol.Add(
					   item.Attributes["key"].Value,
					   item.Attributes["value"].Value);
				}
			}
			catch (Exception)
			{
				nvCol.Clear();
				nvCol = null;
			}
			return nvCol;
		}

		#endregion
	}
}
